package com.lmt.ymcc.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.lmt.ymcc.domain.Course;
import com.lmt.ymcc.domain.CourseMarket;
import com.lmt.ymcc.domain.KillActivity;
import com.lmt.ymcc.domain.KillCourse;
import com.lmt.ymcc.dto.KillParamDto;
import com.lmt.ymcc.dto.PreCourseOrder;
import com.lmt.ymcc.mapper.KillCourseMapper;
import com.lmt.ymcc.service.IKillActivityService;
import com.lmt.ymcc.service.IKillCourseService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.lmt.ymcc.utils.AssertUtil;
import com.lmt.ymcc.utils.CodeGenerateUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import vo.Course2OrderInfoVo;
import vo.Course2OrderVo;

import javax.xml.stream.events.StartElement;
import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author AmazingXu
 * @since 2022-08-23
 */
@Service
@Slf4j
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {

    @Autowired
    private IKillActivityService killActivityService;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    // 添加秒杀课程
    @Override
    public void add(KillCourse killCourse) {
        Long activityId = killCourse.getActivityId();
        KillCourse killCourseTmp = selectByActivityIdAndCourseId(activityId,killCourse.getCourseId());
        AssertUtil.isNull(killCourseTmp ,"课程已经添加到秒杀");
        // 活动状态必须是待发布，才能加入秒杀
        KillActivity killActivity = killActivityService.selectById(activityId);
        AssertUtil.isNotNull(killActivity,"此活动不存在！！");
        boolean isWait = killActivity.getPublishStatus() == KillActivity.STATES_WAIT_PUBLISH;
        AssertUtil.isTrue(isWait,"活动状态异常！");
        // 保存数据
        killCourse.setPublishStatus(killActivity.getPublishStatus());
        killCourse.setStartTime(killActivity.getBeginTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setKillLimit(1);
        killCourse.setCreateTime(new Date());
        insert(killCourse);
    }
    private KillCourse selectByActivityIdAndCourseId(Long activityId, Long courseId) {
        Wrapper<KillCourse> wrapper = new EntityWrapper<>();
        wrapper.eq("course_id",courseId);
        wrapper.eq("activity_id",activityId);
        return selectOne(wrapper);
    }

    /**
    * @Title: onlineAllKillCourse
    * @Description: 课程秒杀
    * @Author: Amazing
    * @Version: 1.0
    * @Date:  2022/8/24 10:51
    * @Parameters: []
    * @Return java.util.List<com.lmt.ymcc.domain.KillCourse>
    */
    @Override
    public List<KillCourse> onlineAllKillCourse() {
        // 找到所有的活动id
        Set<Object> keys = redisTemplate.keys("activity:*");
        AssertUtil.isNotNull(keys, "暂时没有此活动");
        List<KillCourse> killCourses = new ArrayList<>();
        // 遍历key找到每一个课程信息
        keys.forEach(key -> {
            List killCourse = redisTemplate.opsForHash().values(key);
            killCourses.addAll(killCourse);
        });
        // 返回结果集
        return killCourses;
    }

    // 查询秒杀课程详情
    @Override
    public KillCourse onlineOneKillCourse(Long activityId, Long killCourseId) {
        KillCourse killCourse = (KillCourse) redisTemplate.opsForHash().get("activity:" + activityId, killCourseId.toString());
        return killCourse;
    }

    // 执行秒杀，返回预创订单号
    @Override
    public String kill(KillParamDto dto) {
        Date killStartTime = new Date();
        Long killCourseId = dto.getKillCourseId();
        Long activityId = dto.getActivityId();
        // 从redis中查询课程信息
        KillCourse killCourse = onlineOneKillCourse(activityId, killCourseId);
        AssertUtil.isNotNull(killCourse, "商品信息不存在！！");
        String semaphoreKey = activityId + ":" + killCourseId;
        RSemaphore semaphore = redissonClient.getSemaphore(semaphoreKey);
        // 尝试信号量减一
        boolean tryAcquire = semaphore.tryAcquire(1);
        AssertUtil.isTrue(tryAcquire, "很抱歉，课程已被抢完了！！");
        Long loginId = LoginYmccContext.getLoginInfoDto().getId();
        // 生成预创订单信息
        PreCourseOrder preCourseOrder = new PreCourseOrder();
        preCourseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));
        preCourseOrder.setCourseId(killCourse.getCourseId());
        preCourseOrder.setCoursePic(killCourse.getCoursePic());
        preCourseOrder.setCourseName(killCourse.getCourseName());
        preCourseOrder.setTotalCount(1);
        preCourseOrder.setTotalAmount(killCourse.getKillPrice());
        preCourseOrder.setUserId(loginId);
        String orderNo = preCourseOrder.getOrderNo();
        // 保存预创单
        redisTemplate.opsForValue().set(orderNo, preCourseOrder);
        log.info("秒杀执行时间{}", System.currentTimeMillis() - killStartTime.getTime());
        return orderNo;
    }

    // 订单结算页详情数据
    @Override
    public Course2OrderInfoVo oneByOrder(String orderNo) {
        // 参数检验
        AssertUtil.isNotNull(orderNo, "请求不合法");
        // 通过订单号查redis
        Object objTmp =  redisTemplate.opsForValue().get(orderNo);
        AssertUtil.isNotNull(objTmp,"预创单不存在！");
        PreCourseOrder preCourseOrder = (PreCourseOrder) objTmp;
        Course2OrderInfoVo result = new Course2OrderInfoVo();
        List<Course2OrderVo> courseInfos = new ArrayList<>();
        // 设置course信息
        Course course = new Course();
        course.setId(preCourseOrder.getCourseId());
        course.setName(preCourseOrder.getCourseName());
        course.setPic(preCourseOrder.getCoursePic());
        // 设置售卖信息
        CourseMarket courseMarket = new CourseMarket();
        courseMarket.setPrice(preCourseOrder.getTotalAmount());
        courseInfos.add(new Course2OrderVo(course,courseMarket));
        result.setTotalAmount(courseMarket.getPrice());
        result.setCourseInfos(courseInfos);
        return result;
    }
}
